/*
 *	Thread.cpp is part of Flight_Chess_Portable
 *	Copyright (C) 2008  Poison
 *
 *	This program is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 *	Class:			Thread
 *	Description:	
 *	Author:			Poison <hbpoison@gmail.com>
 *	Date Created:	2008-02-26
 */

#include "Thread.h"

#include <cstdio>
#include <cstdlib>
#include <cstring>

//Class sema

Sema::Sema()
{
	char sema_name[16];
	sprintf( sema_name, "sema_%04d", rand() % 10000 );
	create( sema_name );
}

Sema::Sema( const char * name, int init, int max )
{
	create( name, init, max );
}

Sema::~Sema()
{
	drop();
}

int Sema::create( const char * name, int init, int max )
{
	handler = sceKernelCreateSema( name, 0, init, max , NULL );
	return handler;
}

int Sema::drop()
{
	return sceKernelDeleteSema( handler );
}

int Sema::sentSignal( int signal )
{
	return sceKernelSignalSema( handler, signal );
}

int Sema::waitSignal( int signal, unsigned int * timeout )
{
	return sceKernelWaitSema( handler, signal, timeout );
}

//Class callback

Callback::Callback()
{
	handler = -1;
	nCallbackParam.entry = this;
}

Callback::Callback( const char * name, void * arg )
{
	create( name, arg );
}

Callback::~Callback()
{
	drop();
}

int Callback::create( const char * name, void * arg )
{
	handler = -1;
	nCallbackParam.entry = this;
	nCallbackParam.arg = arg;
	handler = sceKernelCreateCallback( name, nCallbackFunction, &nCallbackParam );
	if ( valid() )
		updateInfo();
	return handler;
}

int Callback::drop()
{
	if ( valid() )
		return sceKernelDeleteCallback( handler );
	return -1;
}

int Callback::notify( int arg2 )
{
	return sceKernelNotifyCallback( handler, arg2 );
}

int Callback::cancel()
{
	return sceKernelCancelCallback( handler );
}

int Callback::getCount()
{
	return sceKernelGetCallbackCount( handler );
}

int Callback::updateInfo()
{
	if ( valid() )
	{
		memset( &info, 0, sizeof( SceKernelCallbackInfo ) );
		info.size = sizeof( SceKernelCallbackInfo );
		return sceKernelReferCallbackStatus( handler, &info );
	}
	return -1;
}

int Callback::nCallbackFunction( int arg1, int arg2, void * arg )
{
	CallbackParam * params = ( CallbackParam * )arg;
	return params->entry->CallbackFunction( arg1, arg2, params->arg );
}

int Callback::check()
{
	return sceKernelCheckCallback();
}

//Class thread

Thread::Thread()
{
	sema = NULL;
	handler = -1;
	status = TS_STP;
	nThreadParam.entry = this;
}

Thread::Thread( bool free_on_term, const char * name, int priority, int stack_size, unsigned int attr )
{
	create( free_on_term, name, priority, stack_size, attr );
}

Thread::~Thread()
{
	drop();
}

int Thread::getStackFreeSize()
{
	return sceKernelGetThreadStackFreeSize( handler );
}

int Thread::getExitStatus()
{
	return sceKernelGetThreadExitStatus( handler );
}

int Thread::create( bool free_on_term, const char * name, int priority, int stack_size, unsigned int attr )
{
	sema = NULL;
	handler = -1;
	status = TS_STP;
	nThreadParam.entry = this;
	nThreadParam.fot = free_on_term;
	handler = sceKernelCreateThread( name, ( SceKernelThreadEntry )nThreadEntry, priority, stack_size, attr, NULL );
	if ( valid() )
	{
		updateInfo();
		setupSema();
	}
	return handler;
}

int Thread::drop()
{
	if ( sema )
		delete sema;
	if ( valid() )
	{
		if ( status & TS_RUN )
			terminate();
		return sceKernelDeleteThread( handler );
	}
	return -1;
}

int Thread::start( int argc, void * argp )
{
	if ( valid() )
	{
		nThreadParam.argc = argc;
		nThreadParam.argp = argp;
		return sceKernelStartThread( handler, sizeof( nThreadParam ), &nThreadParam );
	}
	return -1;
}

int Thread::terminate()
{
	return sceKernelTerminateThread( handler );
}

int Thread::terminateDelete()
{
	return sceKernelTerminateDeleteThread( handler );
}

int Thread::suspend()
{
	int ret = sceKernelSuspendThread( handler );
	if ( ret >= 0 )
		status &= TS_SUP;
	return ret;
}

int Thread::resume()
{
	if ( status & TS_SUP )
	{
		int ret = sceKernelResumeThread( handler );
		if ( ret >= 0 )
			status = status & ~TS_SUP;
		return ret;
	}
	return -1;
}

int Thread::wakeup()
{
	return sceKernelWakeupThread( handler );
}

int Thread::cancelWakeup()
{
	return sceKernelCancelWakeupThread( handler );
}

int Thread::waitEnd( unsigned int timeout )
{
	return sceKernelWaitThreadEnd( handler, &timeout );
}

int Thread::waitEndCB( unsigned int timeout)
{
	return sceKernelWaitThreadEndCB( handler, &timeout );
}

int Thread::changePriority( int priority )
{
	return sceKernelChangeThreadPriority( handler, priority );
}

int Thread::releaseWait()
{
	return sceKernelReleaseWaitThread( handler );
}

void Thread::setupSema()
{
	char sema_name[16];
	sprintf( sema_name, "sema_%s", info.name );
	sema = new Sema( sema_name );
}

int Thread::updateInfo()
{
	if ( valid() )
	{
		memset( &info, 0, sizeof( SceKernelThreadInfo ) );
		info.size = sizeof( SceKernelThreadInfo );
		return sceKernelReferThreadStatus( handler, &info );
	}
	return -1;
}

//static funcs
int Thread::exit()
{
	return sceKernelExitDeleteThread( 0 );
}

int Thread::exitDelete()
{
	return sceKernelExitDeleteThread( 0 );
}

int Thread::sleep()
{
	return sceKernelSleepThread();
}

int Thread::sleepCB()
{
	return sceKernelSleepThreadCB();
}

int Thread::delay( unsigned int msec )
{
	return sceKernelDelayThread( msec );
}

int Thread::delayCB( unsigned int msec )
{
	return sceKernelDelayThreadCB( msec );
}

int Thread::changeCurrentAttr( int unk, unsigned int attr )
{
	return sceKernelChangeCurrentThreadAttr( 0, attr );
}

int Thread::getCurrentId()
{
	return sceKernelGetThreadId();
}

int Thread::getCurrentPriority()
{
	return sceKernelGetThreadCurrentPriority();
}

int Thread::nThreadEntry( int argc, void * argp )
{
	ThreadParam * params = ( ThreadParam * )argp;
	params->entry->status = TS_RUN;
	int ret = params->entry->ThreadEntry( params->argc, params->argp );
	params->entry->status = TS_STP;
	if( params->fot )
		delete params->entry;
	return ret;
}
